﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Globalization;

namespace SharePoint.Contrib.Extensions
{
    /// <summary>
    /// Extension methods for the SPList class
    /// </summary>
    public static class SPListExtensions
    {
        /// <summary>
        /// Returns the name (internal name or RootFolder.Name) of the list.
        /// </summary>
        public static string Name(this SPList instance)
        {
            return instance.RootFolder.Name;
        }

        /// <summary>
        /// Determines whether the specified SPList contains a content type.
        /// </summary>
        /// <param name="contentTypeId">The id of the content type</param>
        public static bool ContainsContentType(this SPList instance, SPContentTypeId id)
        {
            SPContentType result;
            return instance.TryFindContentType(id, out result);
        }

        /// <summary>
        /// Determines whether the specified SPList contains a content type and returns that content type if it does.
        /// </summary>
        /// <param name="contentTypeId">The id of the content type</param>
        public static bool TryFindContentType(this SPList instance, SPContentTypeId id, out SPContentType result)
        {
            SPContentTypeId match = instance.ContentTypes.BestMatch(id);

            if (match.IsChildOf(id))
            {
                result = instance.ContentTypes[match];
                return true;
            }

            result = default(SPContentType);
            return false;
        }

        /// <summary>
        /// Adds a content type to the list.
        /// </summary>
        /// <param name="contentTypeId">The content type id.</param>
        /// <remarks>Must call Update() to perform the actual update</remarks>
        public static void AddContentType(this SPList instance, SPContentTypeId id)
        {
            if (instance.ContentTypesEnabled == false)
            {
                instance.ContentTypesEnabled = true;
            }
            
            SPContentType result;

            if (instance.TryFindContentType(id, out result) == false)
            {
                if (instance.ParentWeb.TryFindContentType(id, out result) == false)
                {
                    throw new Exception(string.Format(CultureInfo.CurrentCulture, "Could not add content type '{0}' because it was not found on the parent web", id));
                }

                instance.ContentTypes.Add(result);
            }


        }

        /// <summary>
        /// Removes/Hides all the content types from the list.
        /// </summary>
        public static void ResetAvailableContentTypes(this SPList instance)
        {
            var contenttypes = from SPContentType contenttype in instance.ContentTypes
                               select contenttype;

            DeleteOrHideContentTypes(instance, contenttypes.ToArray());
        }

        /// <summary>
        /// Removes/Hides the content type ids specified from the list.
        /// </summary>
        public static void DeleteOrHideContentTypes(this SPList instance, params SPContentTypeId[] ids)
        {
            SPContentType ct;
            List<SPContentType> contenttypes = new List<SPContentType>(ids.Count());
            foreach (var id in ids)
            {
                if (instance.TryFindContentType(id, out ct))
                {
                    contenttypes.Add(ct);
                }
                else
                {
                    throw new Exception(string.Format("TryFindContentType failed for content type with id '{0}'", id.ToString()));
                }
            }

            DeleteOrHideContentTypes(instance, contenttypes.ToArray());
        }

        /// <summary>
        /// Removes/Hides the content types specified from the list.
        /// </summary>
        public static void DeleteOrHideContentTypes(this SPList instance, params SPContentType[] contentTypes)
        {
            foreach (SPContentType contenttype in contentTypes)
            {
                if (instance.IsContentTypeAllowed(contenttype))
                {
                    try
                    {
                        var ct = instance.ContentTypes[contenttype.Name];
                        if (ct == null)
                        {
                            continue;
                        }
                        ct.Delete();
                        instance.Update();
                    }
                    catch
                    {
                        // Fallback
                        instance.HideContentType(contenttype);
                    }
                }

            }
        }

        /// <summary>
        /// Hides a content type with the specified id from the 'new' button on the list.
        /// </summary>
        public static void HideContentType(this SPList instance, SPContentTypeId id)
        {
            var ct = instance.ParentWeb.AvailableContentTypes[id];

            if (ct == null)
            {
                throw new Exception(string.Format("AvailableContentTypes returned null for content type with id '{0}'", id));
            }

            HideContentType(instance, ct);
        }

        /// <summary>
        /// Hides a content type from the 'new' button on the list.
        /// </summary>
        public static void HideContentType(this SPList instance, SPContentType contentType)
        {
            if (instance.IsContentTypeAllowed(contentType))
            {
                try
                {
                    var ct = instance.ContentTypes[contentType.Name];
                    if (ct == null)
                    {
                        return;
                    }

                    ct.Hidden = true;
                    instance.Update();
                }
                catch (Exception e)
                {
                    string message = string.Format("Failed to hide content type '{0}' on list '{1}' at '{2}'",
                        contentType.Name,
                        instance.Name(),
                        instance.ParentWeb.Url);

                    throw new Exception(message, e);
                }
            }
        }

        /// <summary>
        /// Sets the order of the content types on list.
        /// </summary>
        public static void ContentTypeOrdering(this SPList instance, params SPContentTypeId[] ordering)
        {
            SPContentType ct;
            List<SPContentType> contenttypes = new List<SPContentType>(ordering.Count());
            foreach (var id in ordering)
            {
                if (instance.TryFindContentType(id, out ct))
                {
                    contenttypes.Add(ct);
                }
                else
                {
                    throw new Exception(string.Format("TryFindContentType failed for content type with id '{0}'", id.ToString()));
                }
            }


            ContentTypeOrdering(instance, contenttypes.ToArray());
        }

        /// <summary>
        /// Sets the order of the content types on list.
        /// </summary>
        public static void ContentTypeOrdering(this SPList instance, params SPContentType[] ordering)
        {
            try
            {
                SPFolder folder = instance.RootFolder;

                List<SPContentType> array = new List<SPContentType>(ordering);

                folder.UniqueContentTypeOrder = array;
                folder.Update();
            }
            catch (Exception e)
            {
                string message = string.Format("Failed to set content type ordering on list '{0}' at '{1}'", instance.Name(), instance.ParentWeb.Url);
                throw new Exception(message, e);
            }
        }


    }
}
